<!-- 高阶表单 -->
<script>
import superInput from './super-input.vue'
import headerSpiler from './header-spiler.vue'
import superTable from './super-table.vue'
import superDesc from './super-desc.vue'
const formDataPickFunction = function (params) {
    return _.pickBy(params, (v, k) => {
        if (v instanceof Array) {
            return v.length != 0
        }
        return v !== '' && v != null && v != undefined
    })
}
export default {
    components: { superInput, headerSpiler, superTable, superDesc, },
    props: {
        model: {
            type: Array,
            default: () => [],
        },

        isSupportFold: {
            type: Boolean,
            default: true,
        },
        formDataPick: {
            type: Function,
            default: formDataPickFunction
        },
        formDataHandle: { type: Function, default: (data) => data },
        default: { type: Object, default: () => { return {} } },
        resetFieldFun: null,
        data: { type: Object, default: null },

        loading: Boolean,
        itemStyle: Object,
        itemClass: [Object, String],
        itemAttrs: Object,
        inputStyle: Object,
        inputClass: [Object, String],
        inputAttrs: Object,
        formClass: [Object, String],

        attrs: Object,


        rules: null,

        labelPosition: String,
        labelWidth: String,
        labelSuffix: {
            type: String,
            default: ''
        },
        inline: Boolean,
        inlineMessage: Boolean,
        statusIcon: Boolean,
        showMessage: {
            type: Boolean,
            default: true
        },
        size: String,
        disabled: Boolean,
        validateOnRuleChange: {
            type: Boolean,
            default: true
        },
        hideRequiredAsterisk: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            more: false,
            formData: _.cloneDeep(this.default),
        }
    },

    render: function (h) {
        const props =
            _.pick(this, ['labelPosition', 'rules', 'labelWidth', 'labelSuffix',
                'inline', 'inlineMessage', 'statusIcon', 'showMessage', 'size', 'disabled', 'validateOnRuleChange', 'hideRequiredAsterisk',])

        return h('el-form', {
            ref: 'form', class: this.formClass,
            attrs: { onsubmit: "return false" },
            props: Object.assign({ model: this._realFormData, }, props, this.attrs, this.$attrs),
            directives: [
                { name: 'loading', value: this.loading },
                { name: 'loadingCommonStyle' }
            ]
        }, [
            this.$scopedSlots['form_before'] ? this.$scopedSlots['form_before']({ formData: this._realFormData }) : null,
            ...this.model.filter(item => this._if(item)).map((item, index) => {
                return this._renderForm(h, item, index)
            }),
            this.$scopedSlots['form_append'] ? this.$scopedSlots['form_append']({ formData: this._realFormData }) : null,

            this._isShowMoreButton ? h('el-form-item', {}, [
                h('div', { on: { click: this.moreSwitch }, style: { 'cursor': 'pointer', 'color': '#142142' } }, [
                    this.more ? this.$tt('label_retract') : this.$tt('label_expand'),
                    h('i', { class: { 'el-icon-arrow-down': !this.more, 'el-icon-arrow-up': this.more, 'el-icon--right': true, } }),
                ])
            ]) : null,
            this.$scopedSlots['form_footer'] ? this.$scopedSlots['form_footer']({ formData: this._realFormData }) : null,
        ])
    },
    computed: {
        _realFormData() {
            if (this.data != null) return this.data
            return this.formData
        },
        _isShowMoreButton() {
            if (!this.isSupportFold) return false
            return this.model.some(it => it.fold == true)
        },
    },
    methods: {
        _renderGrid(h, item, index) {
            const list = item.grid.map(r => {
                let rowList = r
                let rowProps = {}
                if (!_.isArray(r)) {
                    const { row, ...rowOther } = r
                    rowList = row
                    rowProps = Object.assign({}, rowOther)
                }
                return {
                    rowProps,
                    rowList: rowList.map(c => {
                        let colItem = c
                        let colProps = {}
                        if (!!c.col) {
                            const { col, ...colOther } = c
                            colItem = col
                            colProps = Object.assign({}, colOther)
                        }
                        if (colProps.span == undefined) {
                            colProps.span = Math.floor(24 / rowList.length)
                        }
                        return { colItem, colProps }
                    }).filter(c => this._if(c.colItem))
                }
            }).filter(r => !_.isEmpty(r.rowList))
            return list.map(r => (
                h('el-row', { props: r.rowProps },
                    r.rowList.map(c => (
                        h('el-col', { props: c.colProps }, [this._renderForm(h, c.colItem, index)])
                    ))
                )
            ))
        },

        _renderForm(h, item, index) {

            const key = item.key || index
            const formType = this._formType(item)
            if (formType == 'renderItem') {
                return item.renderItem(h, { data: this._realFormData, item, index, })
            } else if (item.elRowCol) {
                return h('el-row', { key, }, item.elRowCol.map((it, iti) => {
                    let span = it.span || 1
                    let itemRender = it.col || it
                    return h('el-col', { key: iti, props: { span: span } }, [this._renderForm(h, itemRender, iti)])
                }))
            } else if (formType == "grid") {
                return this._renderGrid(h, item, index)
            } else if (formType == "divider") {
                return h("el-divider", {
                    key,
                    class: item.itemClass,
                    style: item.itemStyle,
                    props: item
                })
            } else if (formType == "header") {
                let directives = []
                if (this.isSupportFold) {
                    directives = [
                        { name: 'show', value: item.fold != true || this.more },
                    ]
                }
                return h(headerSpiler, {
                    key,
                    props: { title: this.$tt(item.header) },
                    class: item.itemClass,
                    style: item.itemStyle,
                    directives,
                })
            } else if (item.slot) {
                if (this.$scopedSlots[item.slot]) return this.$scopedSlots[item.slot]({ formData: this._realFormData, model: item, more: this.more, index: index })
            } else if (formType == 'itemTable') {
                const option = {
                    key,
                    class: item.itemClass,
                    style: item.itemStyle,
                    props: this._propsSuperItem(item, superTable)
                }
                if (item.key) {
                    option.ref = item.key
                    if (!option.props.data) option.props.data = this._realFormData[item.key]
                }
                return h(superTable, option)
            } else if (formType == 'itemDesc') {
                const option = {
                    key,
                    class: item.itemClass,
                    style: item.itemStyle,
                    props: this._propsSuperItem(item, superDesc)
                }
                if (item.key) {
                    option.ref = item.key
                    if (!option.props.data) option.props.data = this._realFormData[item.key]
                } else {
                    if (!option.props.data) option.props.data = this._realFormData
                }
                return h(superDesc, option)
            } else if (formType != 'virtually') {
                let directives = []
                if (this.isSupportFold) {
                    directives = [
                        { name: 'show', value: item.fold != true || this.more },
                    ]
                }
                let labelName = ""
                if (item.label instanceof Function) {
                    labelName = item.label(this._realFormData, item, index)
                } else {
                    labelName = this.$tt(item.label)
                }
                return h('el-form-item', {
                    key,
                    class: this._dealWithClass(item, 'itemClass'),
                    style: this._dealWithStyle(item, 'itemStyle'),
                    props: Object.assign({
                        label: labelName,
                        prop: item.key,
                        rules: this._rules(item),
                    }, this.itemAttrs, item.itemAttrs),
                    directives,
                }, [
                    this._renderFormItem(h, item, index, formType)
                ])

            }

        },
        _renderFormItem(h, item, index, formType) {

            const key = item.key || index
            if (formType == 'render') {
                return item.render(h, { data: this._realFormData, item, index, })
            } else if (formType == 'table') {
                const option = {
                    key,
                    class: item.inputClass,
                    style: item.inputStyle,
                    props: this._propsSuperItem(item, superTable)
                }
                if (item.key) {
                    option.ref = item.key
                    if (!option.props.data) option.props.data = this._realFormData[item.key]
                }
                return h(superTable, option)
            } else if (formType == 'desc') {
                const option = {
                    key,
                    class: item.inputClass,
                    style: item.inputStyle,
                    props: this._propsSuperItem(item, superDesc)
                }
                if (item.key) {
                    option.ref = item.key
                    if (!option.props.data) option.props.data = this._realFormData[item.key]
                } else {
                    if (!option.props.data) option.props.data = this._realFormData
                }
                return h(superDesc, option)
            } else if (formType == 'text') {
                const { formatter } = item
                const value = formatter ? formatter(this._realFormData, item, this._realFormData[item.key], index) : this._realFormData[item.key]
                return h('div', {
                    key,
                    class: this._dealWithClass(item, 'inputClass'),
                    style: this._dealWithStyle(item, 'inputStyle'),
                }, [value])
            } else {
                const props = this._propsSuperInput(item, this.inputAttrs)
                const value =  _.get(this._realFormData, item.key, null)
                if (item.disabled != undefined) props.attrs.disabled = this._disabled(item)
                return h(superInput, {
                    key,
                    class: this._dealWithClass(item, 'inputClass'),
                    style: this._dealWithStyle(item, 'inputStyle'),
                    props: Object.assign({
                        value,
                    }, props),

                    on: this._onSuperInput(item)
                }, [
                    item.keylist ? h(superInput, {
                        style: 'min-width:130px',
                        slot: 'prepend',
                        props: {
                            value:item.key,
                            type: 'select',
                            attrs: {
                                i18n: true,
                                items: item.keylist,
                            }
                        },
                        on: {
                            input: e => {
                                this.$set(item, 'key', e)
                            },
                        }

                    }) : null,
                ])
            }
        },
        _disabled(item) {
            if (item.disabled instanceof Function) {
                return item.disabled(this._realFormData)
            }
            return !!item.disabled
        },
        _if(item) {
            if (item == undefined || item.if == undefined) return true
            if (item.if instanceof Function) {
                return item.if(this._realFormData, item, this._realFormData[item.key])
            }
            return !!item.if
        },
        _rules(item) {
            if (item.rules instanceof Function) {
                return item.rules(this._realFormData, item, this._realFormData[item.key])
            }
            return item.rules
        },
        _formType(item) {
            if (!item) return ""
            if (item.renderItem) return "renderItem"
            if (item.grid) return "grid"

            if (item.render) return "render"
            if (item.divider) return "divider"
            if (item.header) return "header"

            if (item.formType instanceof Function) {
                return item.formType(this._realFormData, item, this._realFormData[item.key])
            }
            return item.formType
        },
        _dealWithClass(item, classKey) {
            let sum = this[classKey]
            let single = item[classKey]
            if (_.isString(sum) && _.isString(single)) {
                return single
            }
            if (_.isString(sum)) {
                let sumlist = _.split(sum, /\s+/)
                sum = _.zipObject(sumlist, _.fill(_.cloneDeep(sumlist), true));
            }
            if (_.isString(single)) {
                let singlelist = _.split(single, /\s+/)
                single = _.zipObject(singlelist, _.fill(_.cloneDeep(singlelist), true));
            }
            return Object.assign({}, sum, single)
        },
        _dealWithStyle(item, styleKey) {
            let sum = this[styleKey]
            let single = item[styleKey]
            if (_.isString(sum) && _.isString(single)) {
                return single
            }
            if (_.isString(sum)) {
                const outlist = _.split(sum, ';')
                const out = {}
                outlist.filter(it => !_.isEmpty(it.trim())).forEach(it => {
                    const kv = _.split(it, ':').filter(it => !_.isEmpty(it.trim()))
                    if (kv.length == 2) {
                        out[kv[0]] = kv[1]
                    }
                })
                sum = out
            }
            if (_.isString(single)) {
                const outlist = _.split(single, ';')
                const out = {}
                outlist.filter(it => !_.isEmpty(it.trim())).forEach(it => {
                    const kv = _.split(it, ':').filter(it => !_.isEmpty(it.trim()))
                    if (kv.length == 2) {
                        out[kv[0]] = kv[1]
                    }
                })
                single = out
            }
            return Object.assign({}, sum, single)
        },
        assignFunction(object, sources) {
            return _.assignWith(object, sources, function (o, s) {
                const newFun = []
                if (o instanceof Array) {
                    newFun.push(...o)
                } else if(o){
                    newFun.push(o)
                }
                if (s instanceof Array) {
                    newFun.push(...s)
                } else if(s){
                    newFun.push(s)
                }
                return newFun
            })
        },
        _vueSet(data, key, e) {
            if (key instanceof Array) {
                if (key.length === 1) {
                    this.$set(data, key[0], e)
                } else {
                    data = data[key.shift()]
                    if (!data) {
                        console.error("_vueSet key not fount", key)
                    } else {
                        this._vueSet(data, key, e)
                    }
                }
            } else if (key.includes('.')) {
                this._vueSet(data, key.split('.'), e)
            } else {
                this.$set(data, key, e)
            }
        },
        _onSuperInput(item,) {
            let on = this.assignFunction({
                input: e => {

                    this._vueSet(this._realFormData, item.key, e)
                },//this.$set(this._realFormData, item.key, e),
            }, item.on,)
            // if (on.input) {
            //     on.input = [on.input, e => this.$set(this._realFormData, item.key, e)]
            // } else {
            //     on.input = e => this.$set(this._realFormData, item.key, e)
            // }

            const keyExtra = item.keyExtra || (item.attrs && item.attrs.keyExtra)

            if (keyExtra) {
                let { _emitKey, ...keyExtraAttr } = keyExtra
                if (!_emitKey) _emitKey = "changeItem"

                const _keyExtraMethod = (it) => {
                    _.forOwn(keyExtraAttr, (value, key) => {
                        if (value instanceof Function) {
                            value(it, this._realFormData)
                        } else {
                            this._vueSet(this._realFormData, value, key === "" ? it : (it ? _.get(it, key, null) : null))
                        }
                    })
                }
                this.assignFunction(on,{[_emitKey]:_keyExtraMethod})
            }
            return on
        },
        _propsSuperInput(item, extra) {
            const { disabled, formatter, ...useitem } = item
            const { label, key, keyExtra, rules, formType, itemClass, itemStyle, itemAttrs, on, keylist, if: _if, fold, slot, ...superInputProps } = useitem
            const { value, type, attrs, ...superInputAttrs } = superInputProps
            return { type, attrs: Object.assign({}, extra, attrs, superInputAttrs), }
        },
        _propsSuperTable(item) {
            const { label, key, keyExtra, rules, formType, itemClass, itemStyle, itemAttrs, on, keylist, if: _if, fold, slot, ...superProps } = item
            const { url, data, model, showIndex, attrs, ...superPropsAttrs } = superProps
            return { url, model, showIndex, attrs: Object.assign({}, attrs, superPropsAttrs), }
        },
        _propsSuperItem(item, el) {
            const { label, key, keyExtra, rules, formType, itemClass, itemStyle, itemAttrs, on, keylist, if: _if, fold, slot, ...superProps } = item
            const prop = _.pickBy(superProps, (v, k) => !!el.props[k])
            prop.attrs = Object.assign({}, prop.attrs, _.pickBy(superProps, (v, k) => !el.props[k]))
            return prop
        },
        set(key, value) {
            this.$set(this.formData, key, value)
        },
        setData(setdata) {
            this.formData = setdata
        },
        //对form组件内部对象进行遍历
        getFormData() {
            const data = this.formDataPick(this._realFormData)
            const out = this.formDataHandle(data)
            return out
        },
        getRef(key) {
            if (key) {
                return this.$refs[key]
            } else {
                return this.$refs.form
            }
        },
        getData() {
            return this._realFormData
        },
        clearValidate(e) {
            return this.$refs.form.clearValidate(e)
        },
        validate(e) {
            return this.$refs.form.validate(e)
        },
        validateField(...e) {
            return this.$refs.form.validateField(...e)
        },
        resetFields(e) {
            let _resetItem = _.cloneDeep(this.default)
            if (this.resetFieldFun instanceof Function) {
                _resetItem = this.resetFieldFun(_resetItem, this._realFormData)
            }
            this.formData = _resetItem

        },
        moreSwitch() {
            this.more = !this.more
        },
    }
}
</script>
<style scoped></style>